package com.mall.module.system.service.Impl;

import com.github.pagehelper.PageHelper;
import com.mall.common.constant.CacheConstants;
import com.mall.common.enums.LogoutReasonEnum;
import com.mall.common.exception.UserException;
import com.mall.common.model.UserAuth;
import com.mall.common.util.UserContext;
import com.mall.framework.service.CacheService;
import com.mall.framework.util.DeviceUtil;
import com.mall.module.system.domain.dto.LoginDto;
import com.mall.module.system.domain.dto.RegisterDto;
import com.mall.module.system.domain.dto.UserQueryDto;
import com.mall.module.system.domain.entity.SysUser;
import com.mall.module.system.domain.vo.LoginVo;
import com.mall.module.system.domain.vo.RegisterVo;
import com.mall.module.system.mapper.SysUserMapper;
import com.mall.module.system.service.SysUserService;
import com.mall.framework.security.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl implements SysUserService {

    private final SysUserMapper userMapper;
    private final AuthenticationManager authenticationManager;
    private final JwtTokenProvider jwtTokenProvider;
    private final PasswordEncoder passwordEncoder;
    private final CacheService cacheService;

    @Override
    @Transactional
    public RegisterVo register(RegisterDto registerDto) {
        // 1. 参数基础校验
        if (registerDto == null) {
            throw new UserException("注册参数不能为空");
        }

        // 2. 检查用户名唯一性
        if (userMapper.existsByUsername(registerDto.getUsername())) {
            throw UserException.usernameExists();
        }
        
        // 处理email字段，保留null值以避免唯一索引冲突
        String email = registerDto.getEmail();
        if (email != null && !email.trim().isEmpty()) {
            // 只检查非空邮箱的唯一性
            if (userMapper.existsByEmail(email)) {
                throw UserException.emailExists();
            }
        } else {
            // 如果是空字符串，显式设置为null
            registerDto.setEmail(null);
        }

        // 处理mobile字段，保持一致性
        String mobile = registerDto.getMobile();
        if (mobile != null && !mobile.trim().isEmpty()) {
            // 只检查非空手机号的唯一性
            if (userMapper.existsByMobile(mobile)) {
                throw new UserException("手机号已存在");
            }
        } else {
            // 如果是空字符串，显式设置为null
            registerDto.setMobile(null);
        }

        // 5. 创建用户实体
        SysUser user = new SysUser();
        BeanUtils.copyProperties(registerDto, user);

        // 6. 密码加密
        user.setPassword(passwordEncoder.encode(registerDto.getPassword()));

        // 7. 设置系统字段
        user.setCreateTime(LocalDateTime.now());
        user.setStatus(1); // 默认启用状态
        user.setUpdateTime(LocalDateTime.now());
        user.setLoginTime(null); // 首次注册无登录时间
        user.setIsDeleted(0); // 默认未删除状态
        user.setLoginCount(0); // 初始登录次数为0

        // 8. 处理其他可选字段
        if (user.getIcon() == null || user.getIcon().trim().isEmpty()) {
            user.setIcon(null); // 设置为null而不是空字符串
        }
        if (user.getNickName() == null || user.getNickName().trim().isEmpty()) {
            user.setNickName(null); // 设置为null而不是空字符串
        }

        // 9. 保存到数据库
        userMapper.insert(user);

        // 10. 返回注册结果
        return RegisterVo.builder()
            .id(user.getId())
            .username(user.getUsername())
            .icon(user.getIcon())
            .email(user.getEmail())
            .mobile(user.getMobile())
            .nickName(user.getNickName())
            .createTime(user.getCreateTime())
            .loginTime(user.getLoginTime())
            .status(user.getStatus())
            .build();
    }

    @Override
    public LoginVo login(LoginDto loginDto) {
        try {
            // 1. 参数校验
            if (loginDto == null) {
                throw new UserException("登录参数不能为空");
            }

            // 2. 执行认证
            Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                    loginDto.getUsername(),
                    loginDto.getPassword()
                )
            );

            // 3. 认证成功，设置认证信息
            SecurityContextHolder.getContext().setAuthentication(authentication);

            // 4. 获取用户信息
            SysUser user = userMapper.selectByUsername(loginDto.getUsername());
            if (user == null) {
                throw UserException.userNotFound();
            }

            // 5. 检查用户状态
            if (user.getStatus() == 0) {
                throw UserException.accountDisabled();
            }

            // 6. 更新登录时间和登录次数
            userMapper.updateLoginTime(user.getId());
            userMapper.incrementLoginCount(user.getId());

            // 7. 生成JWT令牌（会自动存储到Redis）
            String token = jwtTokenProvider.generateToken(authentication);

            // 8. 设置用户上下文
            UserContext.setUserId(user.getId());
            UserContext.setUsername(user.getUsername());

            log.info("用户登录成功，用户ID: {}, 用户名: {}", user.getId(), user.getUsername());

            // 9. 返回登录结果
            return LoginVo.builder()
                .token(token)
                .id(user.getId())
                .username(user.getUsername())
                .icon(user.getIcon())
                .email(user.getEmail())
                .mobile(user.getMobile())
                .nickName(user.getNickName())
                .loginTime(LocalDateTime.now())
                .status(user.getStatus())
                .build();
        } catch (BadCredentialsException e) {
            throw UserException.passwordError();
        }
    }

    //分页查询后台用户数据
    @Override
    public List<SysUser> list(Integer pageNum, Integer pageSize, UserQueryDto queryDto) {
        PageHelper.startPage(pageNum, pageSize);
        return userMapper.list(pageNum, pageSize,queryDto);
    }

    @Override
    public String logout(HttpServletRequest request) {
        // 1. 获取当前认证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw new SecurityException("用户未登录或认证已过期");
        }

        // 2. 获取用户信息
        UserAuth userAuth = (UserAuth) authentication.getPrincipal();
        Long userId = userAuth.getUserId();
        String username = userAuth.getUsername();

        // 3. 获取并销毁Token
        String token = getJwtFromRequest(request);
        String deviceId = DeviceUtil.generateDeviceId(request);

        if (token != null) {
            // 记录登出事件
            recordLogoutEvent(userId, username, token, deviceId, request, LogoutReasonEnum.ACTIVE_LOGOUT);

            // 使Token失效（包括加入黑名单和删除Redis中的Token）
            jwtTokenProvider.invalidateToken(token);

            // 删除设备相关缓存
            cacheService.removeDeviceToken(userId, deviceId);
            cacheService.removeUserSession(userId, deviceId);

            log.info("用户主动退出登录，用户ID: {}, 用户名: {}, 设备ID: {}", userId, username, deviceId);
        }

        // 4. 清除安全上下文
        SecurityContextHolder.clearContext();

        // 5. 清除线程上下文
        UserContext.clear();

        return "退出登录成功";
    }

    /**
     * 强制用户下线
     *
     * @param userId 用户ID
     * @param reason 下线原因
     * @return 操作结果
     */
    public String forceLogout(Long userId, LogoutReasonEnum reason) {
        try {
            // 1. 获取用户所有活跃会话
            List<Map<String, Object>> sessions = cacheService.getUserSessions(userId);

            // 2. 删除所有设备Token和会话
            cacheService.removeAllUserSessions(userId);

            // 3. 将用户所有Token加入黑名单
            String userToken = cacheService.getUserToken(userId);
            if (userToken != null) {
                cacheService.addTokenToBlacklist(userToken, CacheConstants.BLACKLIST_EXPIRE_TIME);
            }

            // 4. 删除用户信息缓存
            cacheService.removeUserInfo(userId);
            cacheService.removeUserToken(userId);

            // 5. 记录强制下线事件
            Map<String, Object> logoutInfo = new HashMap<>();
            logoutInfo.put("userId", userId);
            logoutInfo.put("logoutReason", reason.getCode());
            logoutInfo.put("logoutType", "force_logout");
            logoutInfo.put("logoutTime", LocalDateTime.now());
            logoutInfo.put("remark", "管理员强制下线");
            cacheService.recordLogoutEvent(userId, logoutInfo);

            log.info("用户被强制下线，用户ID: {}, 原因: {}", userId, reason.getDescription());

            return "用户已强制下线";
        } catch (Exception e) {
            log.error("强制用户下线失败，用户ID: {}", userId, e);
            throw new RuntimeException("强制用户下线失败", e);
        }
    }

    /**
     * 获取用户在线设备信息
     *
     * @param userId 用户ID
     * @return 设备信息列表
     */
    public List<Map<String, Object>> getUserOnlineDevices(Long userId) {
        return cacheService.getUserSessions(userId);
    }

    /**
     * 获取用户登出统计
     *
     * @param userId 用户ID
     * @param days   统计天数
     * @return 登出统计信息
     */
    public Map<String, Object> getLogoutStatistics(Long userId, int days) {
        return cacheService.getLogoutStatistics(userId, days);
    }

    @Override
    public void updateUser(Long userId, SysUser user) {
        userMapper.updateById(user);
    }

    @Override
    public SysUser getUserById(Long id) {
        return userMapper.getUserById(id);
    }

    @Override
    public List<SysUser> getUserList(UserQueryDto queryDto) {
        // 这里可以添加分页逻辑
        return userMapper.selectByCondition(convertToUser(queryDto));
    }

    @Override
    public void updateUserStatus(Long id, Integer status, Long updaterId) {
        userMapper.updateStatus(id, status, updaterId);
    }

    @Override
    public void updateUserPassword(Long id, String newPassword, Long updaterId) {
        // 密码加密
        String encodedPassword = passwordEncoder.encode(newPassword);
        userMapper.updatePassword(id, encodedPassword, updaterId);
    }

    @Override
    public void deleteUser(Long id, Long updaterId) {
        userMapper.deleteById(id);
    }

    @Override
    public void batchDeleteUsers(List<Long> ids, Long updaterId) {
        userMapper.batchDelete(ids, updaterId);
    }

    @Override
    public long countUsers(UserQueryDto queryDto) {
        return userMapper.countUsers(convertToUser(queryDto));
    }

    /**
     * 更新用户密码
     *
     * @param userId
     * @param newPassword
     */
    @Override
    public void updatePassword(Long userId, String newPassword) {
        Long CurrentUserId = UserContext.getUserId();
        userMapper.updatePassword(userId, newPassword,CurrentUserId);
    }

    /**
     * 将UserQueryDto转换为SysUser对象
     */
    private SysUser convertToUser(UserQueryDto queryDto) {
        SysUser user = new SysUser();
        user.setUsername(queryDto.getUsername());
        user.setEmail(queryDto.getEmail());
        user.setMobile(queryDto.getMobile());
        user.setNickName(queryDto.getNickName());
        user.setStatus(queryDto.getStatus());
        user.setDeptId(queryDto.getDeptId());
        return user;
    }

    /**
     * 刷新Token
     *
     * @param request HTTP请求
     * @return 新的Token
     */
    public String refreshToken(HttpServletRequest request) {
        String token = getJwtFromRequest(request);
        if (StringUtils.hasText(token)) {
            return jwtTokenProvider.refreshToken(token);
        }
        throw new RuntimeException("Token不能为空");
    }

    /**
     * 记录登出事件
     */
    private void recordLogoutEvent(Long userId, String username, String token, String deviceId,
                                   HttpServletRequest request, LogoutReasonEnum reason) {
        try {
            // 获取设备信息
            String deviceType = DeviceUtil.getDeviceType(request);
            String deviceInfo = DeviceUtil.getDeviceInfo(request);
            String ipAddress = DeviceUtil.getClientIpAddress(request);
            String userAgent = request.getHeader("User-Agent");
            String browser = DeviceUtil.getBrowserInfo(request);
            String os = DeviceUtil.getOperatingSystem(request);

            // 计算会话持续时间（这里简化处理，实际应该从登录时记录开始时间）
            long sessionDuration = 0;

            // 构建登出信息
            Map<String, Object> logoutInfo = new HashMap<>();
            logoutInfo.put("userId", userId);
            logoutInfo.put("username", username);
            logoutInfo.put("token", maskToken(token)); // 脱敏处理
            logoutInfo.put("deviceId", deviceId);
            logoutInfo.put("deviceType", deviceType);
            logoutInfo.put("deviceInfo", deviceInfo);
            logoutInfo.put("ipAddress", ipAddress);
            logoutInfo.put("userAgent", userAgent);
            logoutInfo.put("browser", browser);
            logoutInfo.put("os", os);
            logoutInfo.put("logoutReason", reason.getCode());
            logoutInfo.put("logoutType", "logout");
            logoutInfo.put("logoutTime", LocalDateTime.now());
            logoutInfo.put("sessionDuration", sessionDuration);
            logoutInfo.put("remark", reason.getDescription());

            // 记录到缓存
            cacheService.recordLogoutEvent(userId, logoutInfo);

        } catch (Exception e) {
            log.error("记录登出事件失败，用户ID: {}", userId, e);
        }
    }

    /**
     * Token脱敏处理
     */
    private String maskToken(String token) {
        if (token == null || token.length() < 10) {
            return "***";
        }
        return token.substring(0, 6) + "***" + token.substring(token.length() - 4);
    }

    /**
     * 从请求头获取JWT Token
     */
    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}